package de.fhbrs.fedora;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;

import java.util.Map;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

import de.fhbrs.Logger.LogTypes;
import de.fhbrs.Logger.Logger;


/**
 * DCXMLParser
 *
 * This Class can work with the Doublin Core XML File.
 * 
 * Features:
 * - Get the text of an element
 * - Generate a new XML file (byte[])
 * - Add a new element with text
 * - Change the text of an existing element
 * - Delete an element
 * - Get all elements with its values (text) back  
 *	
 * $Id: DCXMLParser.java 389 2008-05-06 13:23:43Z andreas.moser.bm $
 *
 * @author Andreas Moser
 * @version 1.0
 * @date 06.11.2007
 *
 */
public class DCXMLParser implements XMLParser {
	
	private Document xmlDoc = null;
	
	/**
	 * Constructor will create the header for a new Doublin Core (Fedora) XML file
	 * @throws Exception
	 */
	public DCXMLParser() throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		DocumentBuilder builder = factory.newDocumentBuilder();
		
		// Build an empty XML DOM document for the new Dublin Core
		this.xmlDoc = builder.newDocument();
		Element rootElement=xmlDoc.createElementNS("http://www.openarchives.org/OAI/2.0/oai_dc/","oai_dc:dc");
		rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:oai_dc","http://www.openarchives.org/OAI/2.0/oai_dc/");
		rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:dc","http://purl.org/dc/elements/1.1/");
		xmlDoc.appendChild(rootElement);
	}
	
	/**
	 * Constructor needs the existing Doublin Core (Fedora) XML file
	 * @param dcXML
	 * @throws Exception
	 */
	public DCXMLParser(byte[] dcXML) throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		DocumentBuilder builder = factory.newDocumentBuilder();
		InputStream inputStream = new ByteArrayInputStream(dcXML);
		this.xmlDoc = builder.parse(inputStream);		
	}
	
	/* (non-Javadoc)
	 * @see de.fhbrs.fedora.XMLParser#generateXML()
	 */
	public byte[] generateXML() throws Exception {
		// Use a Transformer for output
		TransformerFactory tFactory = TransformerFactory.newInstance();
		Transformer transformer = tFactory.newTransformer();
		transformer.setOutputProperty(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
		DOMSource source = new DOMSource(this.xmlDoc);
		StringWriter strWriter = new StringWriter();
		StreamResult result = new StreamResult(strWriter);
		transformer.transform(source, result);
		String xmlAsString=strWriter.getBuffer().toString();
		return xmlAsString.getBytes("UTF-8");
	}

	/* (non-Javadoc)
	 * @see de.fhbrs.fedora.XMLParser#getAllAttrVal()
	 */
	/* (non-Javadoc)
	 * @see de.fhbrs.fedora.XMLParser#getAllAttrVal()
	 */
	public Map<String, String> getAllAttrVal() {
		Map<String, String> map = new HashMap<String, String>();
		map.put("Contributor",getContributor());
		map.put("Coverage",getCoverage());
		map.put("Date",getDate());
		map.put("Institute",getInstitute());
		map.put("Inventorynumber",getInventorynumber());
		map.put("ISBN",getISBN());
		map.put("ISSN",getISSN());
		map.put("Journal",getJournal());
		map.put("Page",getPage());
		map.put("Publisher",getPublisher());
		map.put("Shelf",getShelf());
		map.put("Signatur",getSignatur());
		map.put("Source",getSource());
		map.put("Subject",getSubject());
		map.put("Title1",getTitle1());
		map.put("Title2",getTitle2());
		map.put("Type",getType());
		map.put("Vol",getVol());
		map.put("Authors", getValue("dc:creator"));
		return map;
	}
	
	/**
	 * Returns the text content of the element title1  
	 * @return String
	 */
	public String getTitle1(){
		Logger.getInstance().log(LogTypes.INFORMATION, "GetTitle()");
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:title")==0){
				if(metadata.item(a).getTextContent().startsWith("title1=", 0))
					return metadata.item(a).getTextContent().substring(8, metadata.item(a).getTextContent().length());
			}
		}
		return null;
	}
	
	public void setTitle1(String title1){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:title")==0)
				if(metadata.item(a).getTextContent().startsWith("title1=", 0))
					metadata.item(a).setTextContent("title1= " + title1);
		}
	}
	
	/**
	 * Returns the text content of the element title1  
	 * @return String
	 */
	public String getTitle2(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:title")==0)
				if(metadata.item(a).getTextContent().startsWith("title2=", 0))
					return metadata.item(a).getTextContent().substring(8, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setTitle2(String title2){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:title")==0)
				if(metadata.item(a).getTextContent().startsWith("title2=", 0))
					metadata.item(a).setTextContent("title2= " + title2);
		}
	}
	
	/**
	 * Returns the text content of the element title1  
	 * @return String
	 */
	public String getISBN(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:identifier")==0)
				if(metadata.item(a).getTextContent().startsWith("ISBN=", 0))
					return metadata.item(a).getTextContent().substring(6, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setISBN(String isbn){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:identifier")==0)
				if(metadata.item(a).getTextContent().startsWith("ISBN=", 0))
					metadata.item(a).setTextContent("ISBN= " + isbn);
		}
	}
	
	/**
	 * Returns the text content of the element title2  
	 * @return String
	 */
	public String getISSN(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:identifier")==0)
				if(metadata.item(a).getTextContent().startsWith("ISSN=", 0))
					return metadata.item(a).getTextContent().substring(6, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setISSN(String issn){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:identifier")==0)
				if(metadata.item(a).getTextContent().startsWith("ISSN=", 0))
					metadata.item(a).setTextContent("ISSN= " + issn);
		}
	}
	
	/**
	 * Returns the text content of the element signatur  
	 * @return String
	 */
	public String getSignatur(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("signatur=", 0))
					return metadata.item(a).getTextContent().substring(10, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setSignatur(String signatur){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("signatur=", 0))
					metadata.item(a).setTextContent("signatur= " + signatur);
		}
	}
	
	/**
	 * Returns the text content of the element institute  
	 * @return String
	 */
	public String getInstitute(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("institute=", 0))
					return metadata.item(a).getTextContent().substring(11, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setInstitute(String institute){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("institute=", 0))
					metadata.item(a).setTextContent("institute= " + institute);
		}
	}
	
	/**
	 * Returns the text content of the element shelf  
	 * @return String
	 */
	public String getShelf(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("shelf=", 0))
					return metadata.item(a).getTextContent().substring(7, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setShelf(String shelf){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("shelf=", 0))
					metadata.item(a).setTextContent("shelf= " + shelf);
		}
	}
	
	/**
	 * Returns the text content of the element vol  
	 * @return String
	 */
	public String getVol(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("vol=", 0))
					return metadata.item(a).getTextContent().substring(5, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setVol(String vol){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("vol=", 0))
					metadata.item(a).setTextContent("vol= " + vol);
		}
	}
	
	/**
	 * Returns the text content of the element page  
	 * @return String
	 */
	public String getPage(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("page=", 0))
					return metadata.item(a).getTextContent().substring(6, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setPage(String page){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("page=", 0))
					metadata.item(a).setTextContent("page= " + page);
		}
	}
	
	/**
	 * Returns the text content of the element journal  
	 * @return String
	 */
	public String getJournal(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("journal=", 0))
					return metadata.item(a).getTextContent().substring(9, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setJournal(String journal){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("journal=", 0))
					metadata.item(a).setTextContent("journal= " + journal);
		}
	}
	
	/**
	 * Returns the text content of the element inventorynumber  
	 * @return String
	 */
	public String getInventorynumber(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("inventorynumber=", 0))
					return metadata.item(a).getTextContent().substring(17, metadata.item(a).getTextContent().length());
		}
		return null;
	}
	
	public void setInventorynumber(String inventorynumber){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:description")==0)
				if(metadata.item(a).getTextContent().startsWith("inventorynumber=", 0))
					metadata.item(a).setTextContent("inventorynumber= " + inventorynumber);
		}
	}
	
	/**
	 * Returns the text content of the element creator  
	 * @return String[]
	 */
	public String[] getAuthors(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:creator")==0){
				return Utility.convertToArray(metadata.item(a).getTextContent());
			}
		}
		return null;
	}
	
	public void setAuthors(String[] authors){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:creator")==0){
				StringBuffer author = null;
				for(int i=0;i<authors.length;i++){
					author.append("\"" + authors[i] + "\"");
					if(i+1<authors.length){
						author.append(",");
					}
				}
				metadata.item(a).setTextContent(author.toString());
			}
		}
	}
	
	/**
	 * Returns the text content of the element subject  
	 * @return String
	 */
	public String getSubject(){
		return getValue("dc:subject");
	}
	
	public void setSubject(String subject){
		setValue("dc:subject", subject);
	}
	
	/**
	 * Returns the text content of the element publisher  
	 * @return String
	 */
	public String getPublisher(){
		return getValue("dc:publisher");
	}
	
	public void setPublisher(String publisher){
		setValue("dc:publisher", publisher);
	}
	
	/**
	 * Returns the text content of the element contributor  
	 * @return String
	 */
	public String getContributor(){
		return getValue("dc:contributor");
	}
	
	public void setContributor(String contributor){
		setValue("dc:contributor", contributor);
	}
	
	/**
	 * Returns the text content of the element date  
	 * @return String
	 */
	public String getDate(){
		return getValue("dc:date");
	}
	
	public void setDate(String date){
		setValue("dc:date", date);
	}
	
	/**
	 * Returns the text content of the element type  
	 * @return String
	 */
	public String getType(){
		return getValue("dc:type");
	}
	
	public void setType(String type){
		setValue("dc:type", type);
	}
	
	/**
	 * Returns the text content of the element source  
	 * @return String
	 */
	public String getSource(){
		return getValue("dc:source");
	}
	
	public void setSource(String source){
		setValue("dc:source", source);
	}
	
	/**
	 * Returns the text content of the element coverage  
	 * @return String
	 */
	public String getCoverage(){
		return getValue("dc:coverage");
	}
	
	public void setCoverage(String coverage){
		setValue("dc:coverage", coverage);
	}
	
	/**
	 * Returns the text content of the pid  
	 * @return String
	 */
	public String getPid(){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo("dc:identifier")==0)
				if(metadata.item(a).getTextContent().startsWith("lit:", 0))
					return metadata.item(a).getTextContent();
		}
		return null;
	}
	
	/**
	 * Returns the text content of the element element
	 * @param element
	 * @return String
	 */
	private String getValue(String element) {
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		for(int a=1;a<metadata.getLength();a+=2){
			if(metadata.item(a).getNodeName().compareTo(element)==0)
				return metadata.item(a).getTextContent();
		}
		return null;
	}
	
	/**
	 * Set the text text content of the element element
	 * @param element
	 * @param text
	 */
	private void setValue(String element, String text) {
		Node node = getElement(element, 1);
		if (node!=null) {
			node.setTextContent(text);
		} else {
			Element elemNew = xmlDoc.createElement(element);
			Element rootElem = xmlDoc.getDocumentElement();
			rootElem.appendChild(elemNew);
			elemNew.setTextContent(text);
		}
	}
	
	/**
	 * Try to search the Node with the name element
	 * If there is no Node with the name element return null.
	 * @param String element
	 * @return Node
	 */
	private Node getElement(String element, int num){
		Element root = xmlDoc.getDocumentElement();
		NodeList metadata = root.getChildNodes();
		int i = 1;
		for(int a =0; a<metadata.getLength(); a++){
			if(metadata.item(a).getNodeName().equalsIgnoreCase(element))
				if(i == num)
					return metadata.item(a);
				else
					i++;
		}
		return null;
	}
}
